/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package edu.ncsu.external_sensors;

import com.sun.spot.core.resources.ResourceUnavailableException;
import com.sun.spot.edemo.io.ScalarInput;
import com.sun.spot.edemo.peripheral.ADT7411Event;
import com.sun.spot.edemo.peripheral.IADT7411ThresholdListener;
import java.io.IOException;


/**
 *
 * @author David A. Boyuka, II
 * @author Douglas McClusky
 */
public class IRSensor implements IADT7411ThresholdListener  {
	// Note: perfect white is 0, and perfect black is 1024.
	public static final int BLACK_THRESHOLD = 700;
    public  static final int WHITE_THRESHOLD = 300;
    private static final int THRESHOLD = (BLACK_THRESHOLD + WHITE_THRESHOLD) / 2;

	private static final int MIN_VALUE = 0;
	private static final int MAX_VALUE = 1023;

   	private String name;
    private ScalarInput pin;

    private int counter = 0; // Access to counter should be synchronized using "this" as a mutex
    private boolean isHigh;

    private IRSensorListener[] listeners = new IRSensorListener[1];
    private int listenerSize = 0;

    //
    // Constructor
    //

    public IRSensor(String name, ScalarInput pin) {
    	this.name = name;
    	this.pin = pin;

    	resetCounter();
        pin.addIADT7411ThresholdListener(this);
    }

    public void destroy() {
    	pin.removeIADT7411ThresholdListener(this);
    }

    //
    // General public methods
    //

    public synchronized void resetCounter() {
        counter = 0;
        isHigh = isPinHigh();
        setThresholds();
    }

    //
    // IRSensorListener methods
    //

    public synchronized void addIRSensorListener(IRSensorListener listener) {
        if (listenerSize == listeners.length) {
            IRSensorListener[] newListeners = new IRSensorListener[listeners.length*2];
            System.arraycopy(listeners, 0, newListeners, 0, listeners.length);
        }
        listeners[listenerSize++] = listener;
    }

    public synchronized void removeIRSensorListener(IRSensorListener listener) {
        for (int i = 0; i < listenerSize; i++) {
            if (listeners[i] == listener) {
                listeners[i] = listeners[--listenerSize];
                break;
            }
    	}
    }

    private synchronized void counterIncremented() {
        for (int i = listenerSize - 1; i >= 0; i--) { // Go backward so listeners can remove themselves from within the counterIncremeneted() without scrambling the array during this loop
            listeners[i].counterIncremented(this, counter);
        }
    }

    //
    // IScalarInputThresholdListener implementation
    //

	
	public void thresholdExceeded(ADT7411Event evt) {
    	synchronized (this) {
    			counter++;
    			isHigh = isPinHigh();
    			setThresholds();
    	}

        counterIncremented();
	}

	public void thresholdChanged(ADT7411Event evt) {
	}
    //
    // IRobotSensor implementation
    //

	public synchronized double getConvertedValue() {
        return counter;
	}

	public long getRawValue() throws ResourceUnavailableException {
		try {
			return pin.getValue();
		} catch (Exception ioe) {
			throw new ResourceUnavailableException("Error while reading the IR sensor: " + ioe);
		}
	}

	public boolean testConnection() {
		try {
			return pin.getValue() != 0;
		} catch (Exception ioe) {
			return false;
		}
	}

	//
    // Private utility methods
	//
	private boolean isPinHigh() {
    	try {
    		return pin.getValue() > THRESHOLD;
    	} catch (Exception ioe) {
    		return false;
    	}
    }

    private synchronized void setThresholds() {
    	 	if (isHigh) pin.setThresholds(WHITE_THRESHOLD, MAX_VALUE);
    		else		pin.setThresholds(MIN_VALUE, BLACK_THRESHOLD);
            pin.enableThresholdEvents(true);
    }

    public String getPin() {
        return pin.getIndex().toString();
    }

}
